package com.louis.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

public final class MethodUtil {

	private final static Logger								logger			= Logger.getLogger(MethodUtil.class);
	private static Map<Class<?>, Map<String, List<Method>>>	methodsCache	= new HashMap<Class<?>, Map<String, List<Method>>>();

	private MethodUtil() {
	}

	public static Method getMethodFromCache(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
		Method method = null;
		Map<String, List<Method>> map = methodsCache.get(clazz);
		if (map != null) {
			List<Method> list = map.get(methodName);
			if (list != null) {
				for (Method methodCache: list) {
					Class<?>[] parameterTypesCache = methodCache.getParameterTypes();
					if (ArrayUtil.equals(parameterTypesCache, parameterTypes)) {
						method = methodCache;
						break;
					}
				}
			}
		}
		return method;
	}

	public static Method getGetterMethod(Class<?> clazz, String fieldName) {
		String firstCharacterUpperCase = ChangeCaseUtil.firstCharacterUpperCase(fieldName);
		Method method = getMethod(clazz, "get" + firstCharacterUpperCase);
		if (method == null) {
			method = getMethod(clazz, "is" + firstCharacterUpperCase);
		}
		return method;
	}

	public static Method getSetterMethod(Class<?> clazz, String fieldName) {
		String firstCharacterUpperCase = ChangeCaseUtil.firstCharacterUpperCase(fieldName);
		return getMethod(clazz, "set" + firstCharacterUpperCase);
	}

	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
		Method method = getMethodFromCache(clazz, methodName, parameterTypes);
		if (method == null) {
			try {
				method = clazz.getMethod(methodName, parameterTypes);
				if (method != null) {
					Map<String, List<Method>> map = methodsCache.get(clazz);
					if (map == null) {
						map = new HashMap<String, List<Method>>();
						methodsCache.put(clazz, map);
					}
					List<Method> list = map.get(methodName);
					if (list == null) {
						list = new ArrayList<Method>();
						map.put(methodName, list);
					}
					list.add(method);
				}
			} catch (SecurityException e) {
				logger.warn(e.getMessage());
			} catch (NoSuchMethodException e) {
				logger.warn(e.getMessage());
			}

		}
		return method;
	}

	public static Object invoke(Object obj, Method method, Object... parameters) {
		Object object = null;
		try {
			object = method.invoke(obj, parameters);
		} catch (IllegalArgumentException e) {
			logger.warn(e.getMessage());
		} catch (IllegalAccessException e) {
			logger.warn(e.getMessage());
		} catch (InvocationTargetException e) {
			logger.warn(e.getMessage());
		}
		return object;
	}

}
